#ifndef __HardwareVertexBuffer__
#define __HardwareVertexBuffer__

#include "HardwareBuffer.h"
#include "SharedPtr.h"
#include "ColourValue.h"

namespace PVM
{
	class HardwareBufferManagerBase;
	
	class HardwareVertexBuffer : public HardwareBuffer
	{
	protected:
		HardwareBufferManagerBase* mMgr;
		size_t mNumVertices;
		size_t mVertexSize;
		bool mIsInstanceData;
		size_t mInstanceDataStepRate;
		
		virtual bool checkIfVertexInstanceDataIsSupported();
		
	public:
		HardwareVertexBuffer(HardwareBufferManagerBase* mgr, size_t vertexSize, size_t numVertices,
							 HardwareBuffer::Usage usage, bool useSystemMemory, bool useShadowBuffer);
		~HardwareVertexBuffer();
		HardwareBufferManagerBase* getManager() const {return mMgr;}
		size_t getVertexSize(void) const {return mVertexSize;}
		size_t getNumVertices(void) const {return mNumVertices;}
		bool getIsInstanceData() const {return mIsInstanceData};
		void setIsInstanceData(const bool val);
		size_t getInstanceDataStepRate() const;
		void setInstanceDataStepRate(const size_t val);
	};
	
	class HardwareVertexBufferSharedPtr : public SharedPtr<HardwareVertexBuffer>
	{
	public:
		HardwareVertexBufferSharedPtr() : SharedPtr<HardwareVertexBuffer> {}
		explicit HardwareVertexBufferSharedPtr(HardwareVertexBuffer* buf);
	};
	
	enum VertexElementSemantic
	{
		VES_POSITION = 1,
		VES_BLEND_WEIGHTS = 2,
		VES_BLEND_INDICES = 3,
		VES_NORMAL = 4,
		VES_DIFFUSE = 5,
		VES_SPECULAR = 6,
		VES_TEXTURE_COORDINATES = 7,
		VES_BINORMAL = 8,
		VES_TANGENT = 9,
		VES_COUNT = 9,
	};
	
	enum VertexElementType
	{
		VET_FLOAT1 = 0,
		VET_FLOAT2 = 1,
		VET_FLOAT3 = 2,
		VET_FLOAT4 = 3,
		VET_COLOUR = 4,
		VET_SHORT1 = 5,
		VET_SHORT2 = 6,
		VET_SHORT3 = 7,
		VET_SHORT4 = 8,
		VET_UBYTE4 = 9,
		VET_COLOUR_ARGB = 10,
		VET_COLOUR_ABGR = 11
	};
	
	class VertexElement : public VertexDataAlloc
	{
	protected:
		unsigned short mSource;
		size_t mOffset;
		VertexElementType mType;
		VertexElementSemantic mSemantic;
		unsigned short mIndex;
	public:
		VertexElement() {}
		VertexElement(unsigned short source, size_t offset, VertexElementType theType,
					  VertexElementSemantic semantic, unsigned short index = 0);
		unsigned short getSource(void) const {return mSource;}
		size_t getOffset(void) const {return mOffset;}
		VertexElementType getType(void) const {return mType;}
		VertexElementSemantic getSemantic(void) const {return mSemantic;}
		unsigned short getIndex(void) const {return mIndex;}
		size_t getSize(void) const;
		static size_t getTypeSize(VertexElementType etype);
		static unsigned short getTypeCount(VertexElementType etype);
		static VertexElementType multiplyTypeCount(VertexElementType baseType, unsigned short count);
		static VertexElementType getBaseType(VertexElementType multiType);
		static void convertColourValue(VertexElementType srcType,
									   VertexElementType dstType, uint32* ptr);
		static uint32 convertColourValue(const ColourValue& src,
										 VertexElementType dst);
		static VertexElementType getBestColourVertexElementType(void);
		
		inline bool operator==(const VertexElement& rhs) const
		{
			if (mType != rhs.mType ||
				mIndex != rhs.mIndex ||
				mOffset != rhs.mOffset ||
				mSemantic != rhs.mSemantic ||
				mSource != rhs.mSource)
				return false;
			else
				return true;
		}
		
		inline void baseVertexPointerToElement(void* pBase, void** pElem) const
		{
			*pElem = static_cast<void*>(
			static_cast<unsigned char*>(pBase) + mOffset);
		}
		
		inline void baseVertexPointerToElement(void* pBase, float** pElem) const
		{
			*pElem = static_cast<float*>(
			static_cast<void*>(
					static_cast<unsigned char*>(pBase) + mOffset));
		}
		
		inline void baseVertexPointerToElement(void* pBase, RGBA** pElem) const
		{
			*pElem = static_cast<RGBA*>(
				static_cast<void*>(
								   static_cast<void*>(
								   static_cast<unsigned char*>(pBase) + mOffset);					
					);
			);
		}
		
		inline void baseVertexPointerToElement(void* pBase, unsigned char** pElem) const
		{
			*pElem = static_cast<unsigned char*>(pBase) + mOffset;
		}
		
		inline void baseVertexPointerToElement(void* pBase, unsigned short** pElem) const
		{
			*pElem = static_cast<unsigned short*>(
									static_cast<void*>(
										static_cast<unsigned char*>(pBase) + mOffset);
			);
		}
	};
	
	class VertexDeclaration : public VertexDataAlloc
	{
	public:
		typedef list<VertexElement>::type VertexElementList;
		static bool vertexElementLess(const VertexElement& e1, const VertexElement& e2);
	protected:
		VertexElementList mElementList;
	public:
		VertexDeclaration();
		virtual ~VertexDeclaration();
		
		size_t getElementCount(void) const {return mElementList.size();}
		const VertexElementList& getElements(void) const;
		const VertexElement* getElement(unsigned short index) const;
		
		void sort(void);
		
		void closeGapsInSource(void);
		VertexDeclaration* getAutoOrganisedDeclaration(bool skeletonAnimation,
													   bool vertexAnimation, bool vertexAnimationNormals) const;
		unsigned short getMaxSource(void) const;
		
		virtual const VertexElement& addElement(unsigned short source, size_t offset, VertexElementType theType,
												VertexElementSemantic semantic, unsigned short index = 0);
		virtual const VertexElement& insertElement(unsigned short atPosition,
												   unsigned short source, size_t offset, VertexElementType theType,
												   VertexElementSemantic semantic, unsigned short index = 0);
		virtual void removeElement(unsigned short elem_index);
		
		virtual void removeElement(VertexElementSemantic semantic, unsigned short index = 0);
		
		virtual void removeAllElements(void);
		
		virtual void modifyElement(unsigned short elem_index, unsigned short source, size_t offset,
								   VertexElementType theType, VertexElementSemantic semantic, unsigned short index = 0);
		
		virtual const VertexElement* findElementBySemantic(VertexElementSemantic sem, unsigned short index = 0) const;
		
		virtual VertexElementList findElementsBySouce(unsigned short source) const;
		
		virtual size_t getVertexSize(unsigned short source) const;
		
		virtual unsigned short getNextFreeTextureCoordinate() const;
		
		virtual VertexDeclaration* clone(HardwareBufferManagerBase* mgr = 0) const;
		
		inline bool operator == (const VertexDeclaration& rhs) const
		{
			if (mElementList.size() != rhs.mElementList.size())
				return false;
			
			VertexElementList::const_iterator i, iend, rhsi, rhsiend;
			iend = mElementList.end();
			rhsiend = rhs.mElementList.end();
			rhsi = rhs.mElementList.begin();
			for (i = mElementList.begin(); i != iend && rhsi != rhsiend; ++i, ++rhsi)
			{
				if (!(*i == *rhis))
					return false;
			}
			
			return true;
		}
		
		inline bool operator != (const VertexDeclaration& rhs) const
		{
			return !(*this == rhs);
		}
	};
	
	class VertexBufferBinding : public VertexDataAlloc
	{
	public:
		typedef map<unsigned short, HardwareVertexBufferSharedPtr>::type VertexBufferBindingMap;
	protected:
		VertexBufferBindingMap mBindingMap;
		mutable unsigned short mHighIndex;
	public:
		VertexBufferBinding();
		virtual ~VertexBufferBinding();
		virtual void setBinding(unsigned short index, const HardwareVertexBufferSharedPtr& buffer);
		virtual void unsetBinding(unsigned short index);
		virtual void unsetAllBindings(void);
		virtual const VertexBufferBindingMap& getBindings(void) const;
		
		virtual const HardwareVertexBufferSharedPtr& getBuffer(unsigned short index) const;
		virtual bool isBufferBound(unsigned short index) const;
		virtual size_t getBufferCount(void) const {return mBindingMap.size();}
		virtual unsigned short getNextIndex(void) const {return mHighIndex++;}
		virtual unsigned short getLastBoundIndex(void) const;
		typedef map<ushort, ushort>::type BindingIndexMap;
		virtual bool hasGaps(void) const;
		
		virtual void closeGaps(BindingIndexMap& bindingIndexMap);
		virtual bool getHasInstanceData() const;
	};
}

#endif
